﻿Imports System
Imports System.CodeDom
Imports System.Diagnostics.CodeAnalysis
Imports System.Globalization
Imports System.Text
Imports System.Workflow.ComponentModel.Compiler

Namespace System.Workflow.Activities.Rules
    Public Class RuleExpressionWalker
        ' Methods
        Public Shared Sub AnalyzeUsage(ByVal analysis As RuleAnalysis, ByVal expression As CodeExpression, ByVal isRead As Boolean, ByVal isWritten As Boolean, ByVal qualifier As RulePathQualifier)
            If (analysis Is Nothing) Then
                Throw New ArgumentNullException("analysis")
            End If
            RuleExpressionWalker.GetExpression(expression).AnalyzeUsage(expression, analysis, isRead, isWritten, qualifier)
        End Sub

        Public Shared Function Clone(ByVal originalExpression As CodeExpression) As CodeExpression
            If (originalExpression Is Nothing) Then
                Return Nothing
            End If
            Dim result As CodeExpression = RuleExpressionWalker.GetExpression(originalExpression).Clone(originalExpression)
            ConditionHelper.CloneUserData(originalExpression, result)
            Return result
        End Function

        <SuppressMessage("Microsoft.Naming", "CA1720:AvoidTypeNamesInParameters", MessageId:="0#")> _
        Public Shared Sub Decompile(ByVal stringBuilder As StringBuilder, ByVal expression As CodeExpression, ByVal parentExpression As CodeExpression)
            RuleExpressionWalker.GetExpression(expression).Decompile(expression, stringBuilder, parentExpression)
        End Sub

        Public Shared Function Evaluate(ByVal execution As RuleExecution, ByVal expression As CodeExpression) As RuleExpressionResult
            If (execution Is Nothing) Then
                Throw New ArgumentNullException("execution")
            End If
            Return RuleExpressionWalker.GetExpression(expression).Evaluate(expression, execution)
        End Function

        Private Shared Function GetExpression(ByVal expression As CodeExpression) As RuleExpressionInternal
            Dim type As Type = expression.GetType
            Dim length As Integer = RuleExpressionWalker.typeWrappers.Length
            Dim i As Integer
            For i = 0 To length - 1
                Dim tuple As TypeWrapperTuple = RuleExpressionWalker.typeWrappers(i)
                If (type Is tuple.codeDomType) Then
                    Return tuple.internalExpression
                End If
            Next i
            Dim ruleExpr As IRuleExpression = [TryCast](expression,IRuleExpression)
            If (Not ruleExpr Is Nothing) Then
                Return New CustomExpressionWrapper(ruleExpr)
            End If
            Return Nothing
        End Function

        <SuppressMessage("Microsoft.Design", "CA1062:ValidateArgumentsOfPublicMethods")> _
        Public Shared Function Match(ByVal firstExpression As CodeExpression, ByVal secondExpression As CodeExpression) As Boolean
            If ((firstExpression Is Nothing) AndAlso (secondExpression Is Nothing)) Then
                Return True
            End If
            If ((firstExpression Is Nothing) OrElse (secondExpression Is Nothing)) Then
                Return False
            End If
            If (Not firstExpression.GetType Is secondExpression.GetType) Then
                Return False
            End If
            Return RuleExpressionWalker.GetExpression(firstExpression).Match(firstExpression, secondExpression)
        End Function

        Public Shared Function Validate(ByVal validation As RuleValidation, ByVal expression As CodeExpression, ByVal isWritten As Boolean) As RuleExpressionInfo
            If (validation Is Nothing) Then
                Throw New ArgumentNullException("validation")
            End If
            Dim info As RuleExpressionInfo = Nothing
            If Not isWritten Then
                info = validation.ExpressionInfo(expression)
            End If
            If (Not info Is Nothing) Then
                Return info
            End If
            Dim ruleExpr As RuleExpressionInternal = RuleExpressionWalker.GetExpression(expression)
            If (ruleExpr Is Nothing) Then
                Dim item As New ValidationError(String.Format(CultureInfo.CurrentCulture, Messages.CodeExpressionNotHandled, New Object() { expression.GetType.FullName }), &H548)
                item.UserData.Item("ErrorObject") = expression
                If (validation.Errors Is Nothing) Then
                    Dim name As String = String.Empty
                    If ((Not validation.ThisType Is Nothing) AndAlso (Not validation.ThisType.Name Is Nothing)) Then
                        name = validation.ThisType.Name
                    End If
                    Throw New InvalidOperationException(String.Format(CultureInfo.CurrentCulture, Messages.ErrorsCollectionMissing, New Object() { name }))
                End If
                validation.Errors.Add(item)
                Return Nothing
            End If
            Return validation.ValidateSubexpression(expression, ruleExpr, isWritten)
        End Function


        ' Fields
        Private Shared typeWrappers As TypeWrapperTuple() = New TypeWrapperTuple() { New TypeWrapperTuple(GetType(CodeThisReferenceExpression), New ThisExpression), New TypeWrapperTuple(GetType(CodePrimitiveExpression), New PrimitiveExpression), New TypeWrapperTuple(GetType(CodeFieldReferenceExpression), New FieldReferenceExpression), New TypeWrapperTuple(GetType(CodePropertyReferenceExpression), New PropertyReferenceExpression), New TypeWrapperTuple(GetType(CodeBinaryOperatorExpression), New BinaryExpression), New TypeWrapperTuple(GetType(CodeMethodInvokeExpression), New MethodInvokeExpression), New TypeWrapperTuple(GetType(CodeIndexerExpression), New IndexerPropertyExpression), New TypeWrapperTuple(GetType(CodeArrayIndexerExpression), New ArrayIndexerExpression), New TypeWrapperTuple(GetType(CodeDirectionExpression), New DirectionExpression), New TypeWrapperTuple(GetType(CodeTypeReferenceExpression), New TypeReferenceExpression), New TypeWrapperTuple(GetType(CodeCastExpression), New CastExpression), New TypeWrapperTuple(GetType(CodeObjectCreateExpression), New ObjectCreateExpression), New TypeWrapperTuple(GetType(CodeArrayCreateExpression), New ArrayCreateExpression) }

        ' Nested Types
        Private Class CustomExpressionWrapper
            Inherits RuleExpressionInternal
            ' Methods
            Friend Sub New(ByVal ruleExpr As IRuleExpression)
                Me.ruleExpr = ruleExpr
            End Sub

            Friend Overrides Sub AnalyzeUsage(ByVal expression As CodeExpression, ByVal analysis As RuleAnalysis, ByVal isRead As Boolean, ByVal isWritten As Boolean, ByVal qualifier As RulePathQualifier)
                Me.ruleExpr.AnalyzeUsage(analysis, isRead, isWritten, qualifier)
            End Sub

            Friend Overrides Function Clone(ByVal expression As CodeExpression) As CodeExpression
                Return Me.ruleExpr.Clone
            End Function

            Friend Overrides Sub Decompile(ByVal expression As CodeExpression, ByVal decompilation As StringBuilder, ByVal parentExpression As CodeExpression)
                Me.ruleExpr.Decompile(decompilation, parentExpression)
            End Sub

            Friend Overrides Function Evaluate(ByVal expression As CodeExpression, ByVal execution As RuleExecution) As RuleExpressionResult
                Return Me.ruleExpr.Evaluate(execution)
            End Function

            Friend Overrides Function Match(ByVal leftExpression As CodeExpression, ByVal rightExpression As CodeExpression) As Boolean
                Return Me.ruleExpr.Match(rightExpression)
            End Function

            Friend Overrides Function Validate(ByVal expression As CodeExpression, ByVal validation As RuleValidation, ByVal isWritten As Boolean) As RuleExpressionInfo
                Return Me.ruleExpr.Validate(validation, isWritten)
            End Function


            ' Fields
            Private ruleExpr As IRuleExpression
        End Class

        Private Class TypeWrapperTuple
            ' Methods
            Friend Sub New(ByVal type As Type, ByVal internalExpression As RuleExpressionInternal)
                Me.codeDomType = type
                Me.internalExpression = internalExpression
            End Sub


            ' Fields
            Friend codeDomType As Type
            Friend internalExpression As RuleExpressionInternal
        End Class
    End Class
End Namespace

